package com.tanx.cqrs.infrastructure.spring.event.handler;

import com.tanx.cqrs.event.handler.EventHandler;
import com.tanx.cqrs.event.handler.EventHandlerInstance;
import com.tanx.cqrs.event.handler.EventHandlerResolver;
import org.springframework.context.ApplicationContext;

import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 事件处理器的解析器
 */
public class SpringBeanEventHandlerResolverImpl implements EventHandlerResolver {

    private static Set<String> CLASS_NAME_SET = new HashSet<>();
    private ApplicationContext context;
    private Map<Class, List<EventHandlerInstance>> eventHandlerInstanceMap = new ConcurrentHashMap<>();

    public SpringBeanEventHandlerResolverImpl(ApplicationContext context) {
        this.context = context;
    }

    public static boolean hasEventClass(String className) {
        return CLASS_NAME_SET.contains(className);
    }

    @Override
    public void registerAllHandler() {
        Collection<Object> beans = context.getBeansOfType(Object.class).values();
        for (Object bean : beans) {
            List<EventHandlerInstance> list = getEventHandler(bean);
            boolean flag;
            for (EventHandlerInstance instance : list) {
                flag = false;
                List<EventHandlerInstance> eventList = eventHandlerInstanceMap.get(instance.getEventClass());
                if (eventList == null || eventList.isEmpty()) {
                    eventList = new ArrayList<>();
                } else {
                    flag = true;
                }
                eventList.addAll(list);
                if (!flag) {
                    CLASS_NAME_SET.add(instance.getEventClass().getName());
                    eventHandlerInstanceMap.put(instance.getEventClass(), eventList);
                }
            }
        }

    }

    private List<EventHandlerInstance> getEventHandler(Object bean) {
        List<EventHandlerInstance> instanceList = new ArrayList<>();
        Method[] methods = bean.getClass().getMethods();
        for (Method method : methods) {
            EventHandler annotation = method.getAnnotation(EventHandler.class);
            if (annotation == null) {
                continue;
            }
            EventHandlerInstance instance = new SpringBeanEventHandlerInstance(bean, method);
            instanceList.add(instance);
        }

        return instanceList;
    }

    @Override
    public void handlerEvent(Object event) {
        List<EventHandlerInstance> eventHandlerInstances = eventHandlerInstanceMap.get(event.getClass());
        if (eventHandlerInstances.isEmpty()) {
            return;
        }
        eventHandlerInstances.stream().parallel().forEach(eventHandlerInstance -> {
            try {
                eventHandlerInstance.invoke(event);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        });
    }
}
